<!--
 * @Author: Wushiqi
 * @Descripttion: 防抖和节流
 * @Date: 2021-05-06 15:04:31
 * @LastEditor: Wushiqi
 * @LastEditTime: 2021-05-07 10:24:58
-->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>防抖节流</title>
  <div id="box1" class="box1">普通防抖</div>
  <div id="box2" class="box2">立即执行防抖</div>
  <div id="box3" class="box3">时间戳节流</div>
  <div id="box4" class="box4">定时器节流</div>
  <style>
    div[class^=box] {
      width: 250px;
      height: 150px;
      background-color: antiquewhite;
      font-size: 20px;
      line-height: 120px;
      text-align: center;
      margin: 50px auto;
    }
  </style>
</head>
<body>
  <script>
    let box1 = document.getElementById('box1')
    let box2 = document.getElementById('box2')
    let box3 = document.getElementById('box3')
    let box4 = document.getElementById('box4')
    let i1 = 1
    let i2 = 1
    let i3 = 1
    let i4 = 1
    // 防抖：n秒后再执行，如果在n秒内被重复触发，则重新计时
    // 普通防抖，触发后等n秒执行
    function debounce1(func, delay) {
      let timeout
      return function() {
        let arg = arguments
        if (timeout) clearTimeout(timeout) // 如果重复触发，重新计时
        timeout = setTimeout(() => {
          func(arg)
        }, delay)
      }
    }
    box1.addEventListener('mouseover', debounce1(function() {
      box1.innerHTML = i1++
    }, 1000))
    // 立即执行的防抖
    function debounce2(func, delay) {
      let timeout
      return function() {
        let arg = arguments
        if (timeout) clearTimeout(timeout) // 如果在n秒内重复触发，重新计时
        let callNow = !timeout // 还未开始计时前，立即触发一次
        timeout = setTimeout(() => {
          timeout = null // 计时结束后将timeout清空，下一次触发可执行
        }, delay);
        if (callNow) { func(arg) }
      }
    }
    box2.addEventListener('mouseover', debounce2(function() {
      box2.innerHTML = i2++
    }, 1000))
    // 立即执行版和普通版结合
    function debounce3(func, delay, immediate) {
      let timer
      return function() {
        let _this = this
        let arg = arguments
        if (timer) clearTimeout(timer)
        if (immediate) { // 立即执行
          let callNow = !timer
          timer = setTimeout(() => {
            timer = null
          }, delay);
          if (callNow) { func.apply(_this, arg) }
        } else { // 非立即执行
          timer = setTimeout(() => {
            func.apply(_this, arg)
          }, delay);
        }
      }
    }
    // 第三个参数为true表示立即执行，为空或者false表示非立即执行
    // box.addEventListener('mouseover', debounce3(function() {
    //   box.innerHTML = i++
    // }, 2000, true))
    // 节流：在n秒内只执行一次，重复触发只有一次有效
    // 时间戳版
    function throttle1(func, wait) {
      let pre = 0
      return function() {
        let _this = this
        let arg = arguments
        let now = new Date()
        if (now - pre > wait) {
          func.apply(_this, arg)
          pre = now
        }
      }
    }
    box3.addEventListener('mouseover', throttle1(function() {
      box3.innerHTML = i3++
    }, 1000))
    // 定时器版
    function throttle2(func, wait) {
      let timer
      return function() {
        let _this = this
        let arg = arguments
        if (!timer) { // 计时结束之前不进入
          timer = setTimeout(() => {
            timer = null
            func.apply(_this, arg)
          }, wait);
        }
      }
    }
    box4.addEventListener('mouseover', throttle2(function() {
      box4.innerHTML = i4++
    }, 1000))
  </script>
</body>
</html>